const db = require('../../config/database');
const constants = require('../../config/constants');

jest.mock('../../config/database', () => {
  const original = jest.requireActual('../../config/database');
  return {
    ...original,
    pool: {
      getConnection: jest.fn().mockResolvedValue({ release: jest.fn() }),
      execute: jest.fn().mockResolvedValue([[]])
    }
  };
});

describe('Database Utils', () => {
  test('testConnection 正常', async () => {
    // 只要不抛异常就算通过
    await expect(db.testConnection()).resolves.toBeUndefined();
  });

  test('query 正常', async () => {
    const rows = await db.query('SELECT 1+1 AS result');
    expect(Array.isArray(rows)).toBe(true);
  });

  test('query SQL 错误', async () => {
    await expect(db.query('SELECT * FROM not_exist_table')).rejects.toThrow();
  });

  test('transaction 正常', async () => {
    const result = await db.transaction(async (conn) => {
      const [rows] = await conn.query('SELECT 1+2 AS result');
      return rows;
    });
    expect(result).toBeDefined();
  });

  test('transaction 回滚', async () => {
    await expect(db.transaction(async (conn) => {
      throw new Error('test rollback');
    })).rejects.toThrow('test rollback');
  });

  test('testConnection 失败分支', async () => {
    const originalGetConnection = db.pool.getConnection;
    const originalExit = process.exit;
    db.pool.getConnection = jest.fn().mockRejectedValue(new Error('连接失败'));
    process.exit = jest.fn();
    await db.testConnection();
    expect(process.exit).toHaveBeenCalledWith(1);
    db.pool.getConnection = originalGetConnection;
    process.exit = originalExit;
  });

  test('query 抛错时日志输出', async () => {
    const originalExecute = db.pool.execute;
    db.pool.execute = jest.fn().mockRejectedValue(new Error('SQL error'));
    const spy = jest.spyOn(console, 'error').mockImplementation(() => {});
    await expect(db.query('SELECT * FROM error')).rejects.toThrow('SQL error');
    expect(spy).toHaveBeenCalledWith(expect.stringContaining('数据库查询错误:'), expect.any(Error));
    db.pool.execute = originalExecute;
    spy.mockRestore();
  });

  test('transaction rollback/commit/release 抛错', async () => {
    // mock getConnection
    const fakeConn = {
      beginTransaction: jest.fn(),
      commit: jest.fn().mockRejectedValue(new Error('commit error')),
      rollback: jest.fn().mockResolvedValue(),
      release: jest.fn()
    };
    const originalGetConnection = db.pool.getConnection;
    db.pool.getConnection = jest.fn().mockResolvedValue(fakeConn);
    await expect(db.transaction(async () => {})).rejects.toThrow('commit error');
    db.pool.getConnection = originalGetConnection;
  });

  test('transaction rollback itself 抛错', async () => {
    // mock getConnection
    const fakeConn = {
      beginTransaction: jest.fn(),
      commit: jest.fn(),
      rollback: jest.fn().mockRejectedValue(new Error('rollback error')),
      release: jest.fn()
    };
    const originalGetConnection = db.pool.getConnection;
    db.pool.getConnection = jest.fn().mockResolvedValue(fakeConn);
    // callback 抛错，rollback 也抛错
    await expect(db.transaction(async () => { throw new Error('fail'); })).rejects.toThrow('rollback error');
    db.pool.getConnection = originalGetConnection;
  });

  test('transaction release 抛错', async () => {
    // mock getConnection
    const fakeConn = {
      beginTransaction: jest.fn(),
      commit: jest.fn(),
      rollback: jest.fn(),
      release: jest.fn().mockImplementation(() => { throw new Error('release error'); })
    };
    const originalGetConnection = db.pool.getConnection;
    db.pool.getConnection = jest.fn().mockResolvedValue(fakeConn);
    await expect(db.transaction(async () => {})).resolves.toBeDefined(); // release 抛错不影响主流程
    db.pool.getConnection = originalGetConnection;
  });

  test('pool 导出', () => {
    expect(db.pool).toBeDefined();
    expect(typeof db.pool.getConnection).toBe('function');
  });

  test('query 空 SQL', async () => {
    const originalExecute = db.pool.execute;
    db.pool.execute = jest.fn().mockResolvedValue([[]]);
    const rows = await db.query('');
    expect(Array.isArray(rows)).toBe(true);
    db.pool.execute = originalExecute;
  });

  test('transaction callback 抛出非 Error', async () => {
    const fakeConn = {
      beginTransaction: jest.fn(),
      commit: jest.fn(),
      rollback: jest.fn(),
      release: jest.fn()
    };
    const originalGetConnection = db.pool.getConnection;
    db.pool.getConnection = jest.fn().mockResolvedValue(fakeConn);
    await expect(db.transaction(async () => { throw 'string error'; })).rejects.toBe('string error');
    db.pool.getConnection = originalGetConnection;
  });

  test('testConnection release 抛错', async () => {
    const fakeConn = {
      release: jest.fn().mockImplementation(() => { throw new Error('release error'); })
    };
    const originalGetConnection = db.pool.getConnection;
    db.pool.getConnection = jest.fn().mockResolvedValue(fakeConn);
    await expect(db.testConnection()).resolves.toBeUndefined();
    db.pool.getConnection = originalGetConnection;
  });
});

describe('Constants', () => {
  test('APP_NAME', () => {
    expect(constants.APP_NAME).toBe('MyApp');
  });
  test('VERSION', () => {
    expect(constants.VERSION).toBe('1.0.0');
  });
}); 